﻿//-----------------------------------------------------------------------
// <copyright>
//     Copyright (c) Artur Mustafin. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace Expressions.Compiler
{
    using System.Collections;
    using System.Collections.Generic;
    using System.Text;

    using Internal;

    /// <summary>
    /// 
    /// </summary>
    public sealed class Tokens<T> : IEnumerable
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="Rules"/> class.
        /// </summary>
        /// <param name="result">if set to <c>true</c> [result].</param>
        internal Tokens()
        {
            _tokens = new List<Token<T>>();
        }

        private Tokens(List<Token<T>> tokens)
        {
            _tokens = tokens;
        }

        /// <summary>
        /// Implements the operator !.
        /// </summary>
        /// <param name="nonterminal">The input nonterminal</param>
        /// <returns>The result of the operator.</returns>
        public static Tokens<T> operator !(Tokens<T> stream)
        {
            List<Token<T>> tokens = new List<Token<T>>();
            foreach (Token<T> token in stream._tokens)
            {
                tokens.Add(new Token<T>(token.Entity, !token.Optional));
            }
            return new Tokens<T>(tokens);
        }

        /// <summary>
        /// 
        /// </summary>
        private readonly List<Token<T>> _tokens;

        internal IEnumerable<Token<T>> Items
        {
            get
            {
                return _tokens;
            }
        }

        /// <summary>
        /// Parses the specified text.
        /// </summary>
        /// <param name="parser">The input text</param>
        /// <returns>A boolean value</returns>
        public bool Parse(Parser<T> parser)
        {
            Position<T> streamStart = parser.Current;
            List<ExpressionTree<T>> streamTokens = null;
            IEnumerator<Token<T>> enumerator = _tokens.GetEnumerator();
            if (enumerator.MoveNext())
            {
                streamTokens = new List<ExpressionTree<T>>();
                do
                {
                    Token<T> current = enumerator.Current;
                    Entity<T> entity = current.Entity;
                    Position<T> start = parser.Current;
                    if (!entity.Parse(parser))
                    {
                        if (!current.Optional)
                        {
                            return false;
                        }
                    }
                    Position<T> end = parser.Current;
#if TRACE
                    streamTokens.Insert(0, parser.CreateExpressionTree(entity, entity.Name, start, end));
#else
                    streamTokens.Insert(0, parser.CreateExpressionTree(entity, start, end));
#endif
                } while (enumerator.MoveNext());
            }
            Position<T> streamEnd = parser.Current;
#if TRACE
            parser.Parse(streamTokens, Name, streamStart, streamEnd);
#else   
            parser.Parse(streamTokens, streamStart, streamEnd);
#endif            
            return true;    
        }

#if TRACE 
        internal string Name
        {
            get
            {
                return GetName();
            }
        }

        private string GetName()
        {
            StringBuilder sb = new StringBuilder();
            if (_tokens != null)
            {
                IEnumerator<Token<T>> enumerator = _tokens.GetEnumerator();
                if (enumerator.MoveNext())
                {
                    sb.Append(enumerator.Current.Name);
                }
                while (enumerator.MoveNext())
                {
                    sb.Append(' ');
                    sb.Append(enumerator.Current.Name);
                }
            }
            return sb.ToString();
        }

        public override string ToString()
        {
            return GetName();
        }
#endif
        /// <summary>
        /// Adds the specified token.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <param name="optional">if set to <c>true</c> [optional].</param>
        public void Add(Entity<T> token, bool optional = false)
        {
            if (token != null)
            {
                _tokens.Add(new Token<T>(token, optional));
            }
        }

        /// <summary>
        /// Adds the specified token.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <param name="optional">if set to <c>true</c> [optional].</param>
        public void Add(T token, bool optional = false)
        {
            Terminal<T> terminal = (Terminal<T>)token;
            if (terminal != null)
            {
                _tokens.Add(new Token<T>(terminal, optional));
            }
        }

        /// <summary>
        /// Adds the specified token.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <param name="optional">if set to <c>true</c> [optional].</param>
        public void Add(T[] token, bool optional = false)
        {
            Terminal<T> terminal = (Terminal<T>)GetArray(token);
            if (terminal != null)
            {
                _tokens.Add(new Token<T>(terminal, optional));
            }
        }

        private T[] GetArray(T[] tokens)
        {
            List<T> result = new List<T>();
            foreach (T token in tokens)
            {
                result.Add((T)token);
            }
            return result.ToArray();
        }

        /// <summary>
        /// Performs an implicit conversion from <see cref="Expressions.Compiler.Terminal"/> to <see cref="Expressions.Compiler.Tokens"/>.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Tokens<T>(Terminal<T> value)
        {
            Tokens<T> stream = new Tokens<T>();
            if (value != null)
            {
                stream.Add(value);
            }
            return stream;
        }

#if AMIBIGOUS
        /// <summary>
        /// Performs an implicit conversion from <see cref="Expressions.Compiler.Terminal"/> to <see cref="Expressions.Compiler.Tokens"/>.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Tokens(Nonterminal value)
        {
            Tokens stream = new Tokens();
            if (value != null)
            {
                stream.Add(value);
            }
            return stream;
        }
#endif

        /// <summary>
        /// Performs an implicit conversion from <see cref="System.String"/> to <see cref="Expressions.Compiler.Tokens"/>.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Tokens<T>(T[] value)
        {
            return (Terminal<T>)value;
        }

        /// <summary>
        /// Performs an implicit conversion from <see cref="System.Value"/> to <see cref="Expressions.Compiler.Tokens"/>.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Tokens<T>(T value)
        {
            return (Terminal<T>)(new T[] { value });
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="a">A.</param>
        /// <param name="b">The b.</param>
        /// <returns>The result of the operator.</returns>
        public static Tokens<T> operator +(Tokens<T> a, Tokens<T> b)
        {
            Tokens<T> tokens = new Tokens<T>();
            tokens._tokens.AddRange(a._tokens);
            tokens._tokens.AddRange(b._tokens);
            return tokens;
        }

        public IEnumerator GetEnumerator()
        {
            return _tokens.GetEnumerator();
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public sealed class Tokens : IEnumerable
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="Rules"/> class.
        /// </summary>
        /// <param name="result">if set to <c>true</c> [result].</param>
        internal Tokens()
        {
            _tokens = new List<Token>();
        }

        private Tokens(List<Token> tokens)
        {
            _tokens = tokens;
        }

        /// <summary>
        /// Implements the operator !.
        /// </summary>
        /// <param name="nonterminal">The input nonterminal</param>
        /// <returns>The result of the operator.</returns>
        public static Tokens operator !(Tokens stream)
        {
            List<Token> tokens = new List<Token>();
            foreach (Token token in stream._tokens)
            {
                tokens.Add(new Token(token.Entity, !token.Optional));
            }
            return new Tokens(tokens);
        }

        /// <summary>
        /// 
        /// </summary>
        private readonly List<Token> _tokens;

        internal IEnumerable<Token> Items
        {
            get
            {
                return _tokens;
            }
        }

        /// <summary>
        /// Parses the specified text.
        /// </summary>
        /// <param name="parser">The input text</param>
        /// <returns>A boolean value</returns>
        public bool Parse(Parser parser)
        {
            Position streamStart = parser.Current;
            List<ExpressionTree> streamTokens = null;
            IEnumerator<Token> enumerator = _tokens.GetEnumerator();
            if (enumerator.MoveNext())
            {
                streamTokens = new List<ExpressionTree>();
                do
                {
                    Token current = enumerator.Current;
                    Entity entity = current.Entity;
                    Position start = parser.Current;
                    if (!entity.Parse(parser))
                    {
                        if (!current.Optional)
                        {
                            return false;
                        }
                    }
                    Position end = parser.Current;
#if TRACE
                    streamTokens.Insert(0, parser.CreateExpressionTree(entity, entity.Name, start, end));
#else
                    streamTokens.Insert(0, parser.CreateExpressionTree(entity, start, end));
#endif
                } while (enumerator.MoveNext());
            }
            Position streamEnd = parser.Current;
#if TRACE
            parser.Parse(streamTokens, Name, streamStart, streamEnd);
#else
            parser.Parse(streamTokens, streamStart, streamEnd);
#endif
            return true;
        }

#if TRACE
        internal string Name
        {
            get
            {
                return GetName();
            }
        }

        private string GetName()
        {
            StringBuilder sb = new StringBuilder();
            if (_tokens != null)
            {
                IEnumerator<Token> enumerator = _tokens.GetEnumerator();
                if (enumerator.MoveNext())
                {
                    sb.Append(enumerator.Current.Name);
                }
                while (enumerator.MoveNext())
                {
                    sb.Append(' ');
                    sb.Append(enumerator.Current.Name);
                }
            }
            return sb.ToString();
        }

        public override string ToString()
        {
            return GetName();
        }
#endif
        /// <summary>
        /// Adds the specified token.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <param name="optional">if set to <c>true</c> [optional].</param>
        public void Add(Entity token, bool optional = false)
        {
            if (token != null)
            {
                _tokens.Add(new Token(token, optional));
            }
        }

        /// <summary>
        /// Adds the specified token.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <param name="optional">if set to <c>true</c> [optional].</param>
        public void Add(char token, bool optional = false)
        {
            Terminal terminal = (Terminal)token;
            if (terminal != null)
            {
                _tokens.Add(new Token(terminal, optional));
            }
        }

        /// <summary>
        /// Adds the specified token.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <param name="optional">if set to <c>true</c> [optional].</param>
        public void Add(string token, bool optional = false)
        {
            Terminal terminal = (Terminal)token;
            if (terminal != null)
            {
                _tokens.Add(new Token(terminal, optional));
            }
        }

        /// <summary>
        /// Performs an implicit conversion from <see cref="Expressions.Compiler.Terminal"/> to <see cref="Expressions.Compiler.Tokens"/>.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Tokens(Terminal value)
        {
            Tokens stream = new Tokens();
            if (value != null)
            {
                stream.Add(value);
            }
            return stream;
        }

#if AMIBIGOUS
        /// <summary>
        /// Performs an implicit conversion from <see cref="Expressions.Compiler.Terminal"/> to <see cref="Expressions.Compiler.Tokens"/>.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Tokens(Nonterminal value)
        {
            Tokens stream = new Tokens();
            if (value != null)
            {
                stream.Add(value);
            }
            return stream;
        }
#endif

        /// <summary>
        /// Performs an implicit conversion from <see cref="System.String"/> to <see cref="Expressions.Compiler.Tokens"/>.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Tokens(string value)
        {
            return (Terminal)value;
        }

        /// <summary>
        /// Performs an implicit conversion from <see cref="System.Char"/> to <see cref="Expressions.Compiler.Tokens"/>.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Tokens(char value)
        {
            return (Terminal)value.ToString();
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="a">A.</param>
        /// <param name="b">The b.</param>
        /// <returns>The result of the operator.</returns>
        public static Tokens operator +(Tokens a, Tokens b)
        {
            Tokens tokens = new Tokens();
            tokens._tokens.AddRange(a._tokens);
            tokens._tokens.AddRange(b._tokens);
            return tokens;
        }

        public IEnumerator GetEnumerator()
        {
            return _tokens.GetEnumerator();
        }
    }
}
